﻿using System.Collections;
using System.Collections.Generic;
using Dune.Business.Managers;
using Dune.Common;
using Dune.Common.Utils;
using Dune.DAL.Common;
using NHibernate;
using NHibernate.Expression;

namespace Dune.DAL.Managers
{
    class AbstractManager<T, IT> : BaseManager, IAbstractManager<T, IT>
    {
        public virtual T Load(IT id)
        {
            T ret;

            using (IDalOperation op = CreateLoadOperation())
            {
                ret = Session.Load<T>(id);
                op.Commit();
            }

            return ret;
        }

        public virtual T LoadCacheble(IT id)
        {
            T ret;

            using (IDalOperation op = CreateLoadOperation())
            {
                ICriteria criteria = CreateCriteria();
                criteria.Add(Expression.Eq("Id", id));
                criteria.SetCacheable(true);
                ret = criteria.UniqueResult<T>();
                op.Commit();
            }

            return ret;
        }

        public virtual void Save(T obj)
        {
            using (IDalOperation op = CreateSaveOperation())
            {
                Session.Save(obj);
                op.Commit();
            }
        }

        public virtual void Update(T obj)
        {
            using (IDalOperation op = CreateSaveOperation())
            {
                Session.Update(obj);
                op.Commit();
            }
        }

        public virtual void Delete(T obj)
        {
            using (IDalOperation op = CreateSaveOperation())
            {
                Session.Delete(obj);
                op.Commit();
            }
        }

        public virtual ICollection<T> FindAll()
        {
            return FindAll(false);
        }

        public ICollection<T> FindAll(bool cacheble)
        {
            IList<T> ret = null;
            using (IDalOperation op = CreateLoadOperation())
            {
                ICriteria criteria = CreateCriteria();
                if (cacheble)
                    criteria.SetCacheable(true);
                ret = criteria.List<T>();
                op.Commit();
            }
            return ret;
        }

        protected ICriteria CreateCriteria(FetchOptions fetchOptions)
        {
            ICriteria criteria = Session.CreateCriteria(typeof(T));

            criteria = criteria.SetFirstResult(fetchOptions.FirstResult);
            if (fetchOptions.MaxResults > 0)
                criteria = criteria.SetMaxResults(fetchOptions.MaxResults);
            if (!StringUtils.IsBlankOrNull(fetchOptions.SortExpression))
            {
                SetupSortOptions(criteria, fetchOptions);
            }
            return criteria;
        }

        protected virtual void SetupSortOptions(ICriteria criteria, FetchOptions fetchOptions)
        {
            Order order = fetchOptions.SortDirection == SortDirection.Ascending ?
                  Order.Asc(fetchOptions.SortExpression) :
                  Order.Desc(fetchOptions.SortExpression);

            criteria = criteria.AddOrder(order);
        }

        protected ICriteria CreateCriteria()
        {
            ICriteria criteria = Session.CreateCriteria(typeof(T));
            return criteria;
        }

        #region Find/Count

        public ICollection<T> Find(FetchOptions fetchOptions)
        {
            IList result;
            using (IDalOperation op = CreateLoadOperation())
            {
                result = CreateCriteria(fetchOptions).List();
                op.Commit();
            }
            return CollectionsUtils.CreateList<T>(result);
        }

        public int Count()
        {
            ICriteria criteria = CreateCriteria();
            criteria.SetProjection(Projections.RowCount());
            return (int)criteria.List()[0];
        }

        #endregion

        protected int GetCount(ICriteria criteria)
        {
            criteria.SetProjection(Projections.RowCount());
            return (int)criteria.List()[0];
        }

    }
}
